/**
 *
 */
package idao.engine.executor;

import idao.DaoRuntimeException;
import idao.Reusable;
import idao.RowHandler;
import idao.engine.DaoUtils;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Map;

import ognl.Ognl;
import ognl.OgnlException;
import ognl.OgnlRuntime;

/**
 * @author huhao
 */
@Reusable
class DefaultRowHandler implements RowHandler
{

    private Class<?> requiredType;
    private boolean simpleType;

    protected DefaultRowHandler(Class<?> requiredType)
    {
        this.requiredType = requiredType;
        this.simpleType = DaoUtils.isSimpleType(requiredType);
    }

    public Object handleRow(ResultSet rs, int rowNum) throws SQLException
    {
        return simpleType ? this.extractSimpleValue(rs) : this.extractBean(rs);
    }

    private Object extractSimpleValue(ResultSet rs) throws SQLException
    {
        return DaoUtils.getValue(rs, 1, requiredType);
    }

    private Object extractBean(ResultSet rs) throws SQLException
    {
        Object bean = DaoUtils.newInstance(requiredType, null);
        ResultSetMetaData rsmd = rs.getMetaData();
        for (int i = 1, count = rsmd.getColumnCount() + 1; i < count; i++) {
            // take column name as simple property name of the bean class, ignorecase
            String columnName = rsmd.getColumnLabel(i);
            try {
                @SuppressWarnings("unchecked")
                Map<String, PropertyDescriptor> pds = OgnlRuntime
                                .getPropertyDescriptors(requiredType);
                for (Map.Entry<String, PropertyDescriptor> pdEntry : pds.entrySet()) {
                    // Is there a matching writable Property
                    String propName = pdEntry.getKey();
                    PropertyDescriptor pd = pdEntry.getValue();
                    if (propName.equalsIgnoreCase(columnName) && (pd.getWriteMethod() != null)) {
                        Object value = DaoUtils.getValue(rs, i, pd.getPropertyType());
                        Ognl.setValue(propName, bean, value);
                    } else {
                        // no matching property or not writable
                        // TODO should we log this?
                    }
                }
            } catch (IntrospectionException e) {
                throw new DaoRuntimeException("Failed to populate bean object", e);
            } catch (OgnlException e) {
                throw new DaoRuntimeException("Failed to populate bean object", e);
            }
        }
        return bean;
    }
}
